/*
 *    Copyright (c) 2006 ThoughtWorks, Inc.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package com.thoughtworks.turtlemock.internal;

import com.thoughtworks.turtlemock.constraint.CheckResult;
import com.thoughtworks.turtlemock.constraint.Constraint;
import junit.framework.Assert;

public class OnceInvokeMethodResultLog extends BaseInvokeMethodResultLog {
    private final Object[] params;
    private int times = 1;

    public OnceInvokeMethodResultLog(String operationName, Object[] params) {
        super(operationName);
        this.params = params == null ? new Object[0] : params;
    }

    public InvokeWithArgsResultLog withArgsConstraint(Constraint[] constraints) {
        CheckResult result = CheckResult.PASS;
        if (constraints != null && constraints.length > 0) {
            for (int i = 0; i < constraints.length; i++) {
                result = result.append(withArgConstraint(constraints[i], i).status());
            }
            if (result.isFullFilled()) {
                return new DefaultInvokeWithArgsResultLog(this, result);
            }
        }
        return new EmptyInvokeWithArgsResultLog(result);
    }

    public InvokeWithArgsResultLog withAllArgsConstraint(Constraint constraint) {
        return check(constraint.check(params));
    }

    public InvokeWithArgsResultLog withArgConstraint(Constraint constraint, int paramIndex) {
        boolean paramIndexInBounds = paramIndex < params.length && paramIndex >= 0;
        String msg = paramIndexInBounds ? "param index(" + paramIndex + ") exists" : "param index(" + paramIndex + ") out of params size Bounds(" + params.length + ")";
        CheckResult outOfBoundsResult = new CheckResult(msg, paramIndexInBounds);
        CheckResult result = paramIndexInBounds ? constraint.check(params[paramIndex]) : outOfBoundsResult;
        return check(result);
    }

    private InvokeWithArgsResultLog check(CheckResult result) {
        if (result.isFullFilled()) {
            return new DefaultInvokeWithArgsResultLog(this, result);
        }
        return new EmptyInvokeWithArgsResultLog(result);
    }

    public InvokeWithArgsResultLog noArgs() {
        boolean fullFilled = params == null || params.length == 0;
        String msg = "expected param size is 0, actual was " + params.length;
        return check(new CheckResult(msg, fullFilled));
    }

    public void times(int expectedTimes) {
        String msg = "expected invoke " + getOperationName() + " " + expectedTimes + " times, actual was: " + times;
        boolean check = (expectedTimes == 0) ? times == 0 : expectedTimes <= times;

        Assert.assertTrue(msg, check);

        this.times -= expectedTimes;
    }

    public int timesNotAsserted() {
        return times;
    }

    public void rollback() {
        this.times = 1;
    }
}
